# VUE Passing Data

single source of truth…

props -› pass data from parent to child: downwards

emit custom Events -› data from component to main-app: upwards

# Props

Parent => child

Props = properties. Act like custom HTML-Attributes

Each component has its own isolated scope. Props are custom attributes for passing data into a component.

Props should be defined in advance. (also define type, required etc.)

simplest form: an Array (props as strings) (more for prototyping)

  • (camelCase): vue altomatically converts camelCase to kebap-case. (HTML-template should use kebap)
  • props are available just like data: use in template and in script with .this
  • make sure to have no name clashes with data- and computed-properties

in parent - pass the data via attribute:

  <Modal header="Sign up now"/>

in child: register the prop (has to be a string)

  export default {
      props: ['header'],
  };

use in child:

<h1>{{ header }}</h1>

# Multiple Props

<Modal header="Sign up now !" text="Grab it for half price"/>
props: ['header', 'text'],

# Data-Bind

to pass data that is not a string - eg an Array, variables, etc.

In Parent:

<Modal :header="header" :text="text"/>

# Class data-bind

parent:

<Modal :header="header" :text="text" theme="sale"/>

child

if condition is true, apply 'sale'

<div class="modal" :class="{ sale: theme === 'sale'}">
export default {
	props: ['header', 'text', 'theme'],
};

# Props should not be mutated!

-> unidirectional Data-flow.

2 ways to handle it:

  1. send event to the parent and chnge the data there
  2. Use as initial value: make a copy and change it in the component, but it will not change th original data from the parent!
props: ['isFavorite'], 
data() { return { friendIsFavorite: this.isFavorite, }

# Validating Props

Using an Array is only ok with very simple projects, prototypes etc

props: ['name', 'phoneNumber', 'emailAdress'],
// This is only OK when prototyping

Vue’s props feature has built-in validation, so we can specify things like the prop’s type and whether it’s required, etc.

Use an Object and provide the type:

props: { 
  name: String, 
  phoneNumber: String,
  emailAddress: String, 
  isFavorite: String 
}

or even more detailled: provide an object for every prop

props: { 
	name: { 
		type: String, 
		required: true, 
	}, 
	isPremium: { 
		type: Boolean,
		required: false, 
	}, 
	isFavorite: { 
		type: Boolean, 
		required: false, 
		default: false, 
		validator(value) { 
			// check... // return true or false 
		}, 
}

default can also be a function

default: function() {}

default() {
 //
},

åadd a validator check:

validator(value) {
	// check...
	// return true or false
},

https://v3.vuejs.org/guide/component-props.html#prop-types

Specifically, the following value types (type property) are supported:

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol

But type can also be any constructor function (built-in ones like Date or custom ones).

# Dynamic Prop Values

If the prop needs a Non-String-Value, you have to v-bind it:

:isFavorite="true"

but also to make the content dynamic, to use v-for, v-if

<friend-contact
	v-for="friend in friends"
	:key="friend.id"
	:name="friend.name"
</friend-contact>

# Emit

child => parent

Custom_event: fired from a component, can be listened from the parent-component

  • up: emit an event, telling the parent that it happened.
  • Give the component the ability to let its parent know an event happened within it.
  • Custom Events are emmited (via $emit) to trigger a method in a parent component
# emit the event
// child
methods: {
	closeModal() {
		this.$emit('closeModal')
	}
}

parent - can listen to the custom event

// parent
<Modal :header="header" :text="text" theme="sale" @closeModal="toggleModal"/>

the custom event will fire from the child to the parent

  • they can contain data (eg. data the user has entered, the id of a selected element, etc.)

No Neighbour communication!! -> you have to use the parent component

use this.$emit

  • Emit needs at least one Argument: the name of the event
  • convention: use kebap-case
methods: { addToCart() { this.$emit('add-to-cart') } ... }

-> listen for that event from within the parent scope,

<product-display @add-to-cart="updateCart"></product-display>

When that event is “heard” by the parent, it will trigger a new method by the name of updateCart

You can add as many arguments as you want: that is data that you pass together with the event

the emit can contain data:

addToCart() { this.$emit('add-to-cart', this.variants[this.selectedVariant].id)
},

-> id is the payload

// child
toggleFavorite() {
	this.$emit('toggle-favorite', this.id);
},
// parent
  methods: {
    toggleFavoriteStatus(friendId) {
      const identifiedFriend = this.friends.find(
        (friend) => friend.id === friendId
      );
      identifiedFriend.isFavorite = !identifiedFriend.isFavorite;
    },
  },

# Defining & validating custom Events

use emits-property

-> especially to document the component

emits: ['toggle-favorite'],

you can also specify an object:

then use a function, that recieves the data you will emit as parameters. and for example add validation in the function

emits: {
    'toggle-favorite': function(id) {
      if (id) {
        return true;
      } else {
        console.warn('Id is missing');
        return false;
      }
    },
  },

# Prop Fallthrough

You can set props (and listen to events) on a component which you haven't registered inside of that component.

Vue has built-in support for prop (and event) "fallthrough".

You can get access to these fallthrough props on a built-in $attrs property (e.g. this.$attrs).

https://v3.vuejs.org/guide/component-attrs.html


# Provide & Inject

If data has to travel though a lot of "pass-through-components", you can use Provide-inject.

Provide data in the parent component and pass it directly to any child-component, no matter how deeply nested it is.

use provide & inject only when nescessary. to avoid pass-through-components. It makes the code less understandable. props & custom rvrnts should be the default communication

If you have to pass date or "grandchild" etc."

Provide in one place and use it in another place -> avoid pass-through-components

Provide in a parent or higher component (not in a neighbour)

provide: {
    topics: [
      {
        id: 'basics',
        title: 'The Basics',
        description: 'Core Vue basics you have to know',
        fullText:
          'Vue is a great framework and it has a couple of key concepts: Data binding, events, components and reactivity - that should tell you something!'
      },
    ]
  },

Better: use a method, that returns an object -> you can access data from the data-property

 provide() {
    return {
      topics: this.topics
    };
  },

inject works like props.

inject: ['topics'],

you can also provide methods:

# inject Function

// child
<button @click="selectTopic(id)">Learn More</button>

// ...
export default {
  inject: ['selectTopic'],
// parent

 provide() {
    return {
         selectTopic: this.activateTopic
    };
  },
   methods: {
    activateTopic(topicId) {
      this.activeTopic = this.topics.find(topic => topic.id === topicId);
    }
  }